﻿#include "videoplaythread.h"

extern "C"
{
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libavutil/pixfmt.h"
    #include "libswscale/swscale.h"
}

VideoPlayThread::VideoPlayThread(QWidget *parent)
{
    avformat_network_init();   // 初始化FFmpeg网络模块
    av_register_all();         // 初始化FFmpeg
}

VideoPlayThread::~VideoPlayThread()
{

}

void VideoPlayThread::StratPlay(QString strVideo)
{
    m_strVideoPath = strVideo;
    this->start();
}

void VideoPlayThread::StopPlay()
{

}

void VideoPlayThread::run()
{
    AVFormatContext *pFormatCtx;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVFrame *pFrame, *pFrameRGB;
    AVPacket *packet;
    uint8_t *out_buffer;

    static struct SwsContext *img_convert_ctx;

    int nVideoStream = 0;
    int nNumBytes = 0;

    pFormatCtx = avformat_alloc_context();

    AVDictionary *avdic = NULL;
    char option_key[] = "rtsp_transport";
    char option_value[] = "tcp";
    av_dict_set(&avdic, option_key, option_value, 0);

    char option_key2[] = "max_delay";
    char option_value2[] = "100";
    av_dict_set(&avdic, option_key2, option_value2, 0);

    if (avformat_open_input(&pFormatCtx, m_strVideoPath.toStdString().c_str(), NULL, &avdic) != 0)
    {
        printf("can't open the file. \n");
        return;
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
    {
        printf("Could't find stream infomation.\n");
        return;
    }

    nVideoStream = -1;

    for (int i = 0; i < pFormatCtx->nb_streams; i++)
    {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            nVideoStream = i;
        }
    }

    if (nVideoStream == -1)
    {
        printf("Didn't find a video stream.\n");
        return;
    }

    // 查找解码器
    pCodecCtx = pFormatCtx->streams[nVideoStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);

    pCodecCtx->bit_rate = 0;       // 初始化为0
    pCodecCtx->time_base.num = 1;  // 下面两行：一秒钟25帧
    pCodecCtx->time_base.den = 10;
    pCodecCtx->frame_number = 1;   // 每包一个视频帧

    if (pCodec == NULL)
    {
        printf("Codec not found.\n");
        return;
    }

    // 打开解码器
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
        printf("Could not open codec.\n");
        return;
    }

    pFrame = av_frame_alloc();
    pFrameRGB = av_frame_alloc();

    // 这里我们改成了 将解码后的YUV数据转换成RGB32
    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
                                        pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
                                        AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);

    nNumBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height);

    out_buffer = (uint8_t *)av_malloc(nNumBytes * sizeof(uint8_t));
    avpicture_fill((AVPicture *)pFrameRGB, out_buffer, AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height);

    int y_size = pCodecCtx->width * pCodecCtx->height;

    packet = (AVPacket *)malloc(sizeof(AVPacket));  // 分配一个packet
    av_new_packet(packet, y_size);                  // 分配packet的数据

    while (1)
    {
        if (av_read_frame(pFormatCtx, packet) < 0)
            break; //这里认为视频读取完了

        if (packet->stream_index == nVideoStream)
        {

            int nGotPicture = 0;
            int nRet = avcodec_decode_video2(pCodecCtx, pFrame, &nGotPicture, packet);
            if (nRet < 0)
            {
                printf("decode error.\n");
                return;
            }

            if (nGotPicture)
            {
                sws_scale(img_convert_ctx,
                                         (uint8_t const * const *) pFrame->data,
                                          pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
                                          pFrameRGB->linesize);

                // 把这个RGB数据 用QImage加载
                QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
                QImage image = tmpImg.copy();   // 把图像复制一份 传递给界面显示
                emit SignalFrame(image);        // 发送信号
            }
        }

        av_free_packet(packet); // 释放资源,否则内存会一直上升
        msleep(1);
    }

    av_free(out_buffer);
    av_free(pFrameRGB);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
}
